#include <stdint.h>
#include <stdfunc.h>
#include <text_video.h>
#include <memory.h>
#include <task.h>
#include <keyboard.h>
#include <shell.h>

static uint8_t key_buf[256];
static uint8_t key_cmd_buf[256];

volatile uint32_t buf_pos_tail, buf_pos_head;

static inline void shell_kbd_listener(uint8_t key)
{    
    key_buf[buf_pos_head++] = key; key_buf[buf_pos_head] = 0;
}

static inline uint8_t get_kbd_key(void)
{
    if(buf_pos_tail >= buf_pos_head){return 0;}
    
    return key_buf[buf_pos_tail++];
}

static void parse_shell_command(void)
{
    uint32_t pos;
    
    for(pos = 0; pos < command_pos; pos++)
    {
        if(!compare_memory(key_cmd_buf, (uint8_t*)command_name_buf[pos], max((uint32_t)strlen((char*)key_cmd_buf), (uint32_t)strlen(command_name_buf[pos]))))
        {
            command_func_buf[pos]();
        }
    }
}

void add_shell_command(const char * name, shellCMD func)
{
    command_func_buf[command_pos] = func;
    command_name_buf[command_pos] = (char*)memalloc(strlen(name)+1, 0);
    copy_memory((uint8_t*)command_name_buf[command_pos], (uint8_t*)name, strlen(name)+1);
    
    printf("SHELL: adding command \"%s\"\n", command_name_buf[command_pos]);
    
    command_pos++;
}

void wait_for_key(const char ch)
{
    uint8_t key;
    
    while(1)
    {
        GUTS();
        key = get_kbd_key();
        if(key == ch){return;}
        else{}
    } 
}

uint8_t get_key(void)
{
    uint8_t key;
    
    while(1)
    {
        GUTS();
        key = get_kbd_key();
        if(key){return key;}
        else{}
    } 
}

void shell_task(void)
{
    uint8_t key;
    uint32_t BPOS;
    
    GUTS();GUTS();GUTS();                                   // let things finish before we start
    
    clear_memory((uint8_t*)key_buf, 256);                   // clear out our key buffer
    
    buf_pos_tail = buf_pos_head = 0;                        // set the key buffer positions to zero
    command_pos = 0;                                        // no commands have been set yet
    keyboard_register_listener(shell_kbd_listener);         // register ourselves as a keyboard listener
    
    init_standard_commands();
    
    cls();
    
    info_bar_printf("Type \"help\" for the list of available commands");
    printf("#] ");
        
    BPOS = 0;
    while(1)
    {
        GUTS();
        while(1)
        {
            while(1)
            {
                key = get_kbd_key();
                if(key){break;}else{GUTS();}
            }
            
            if(key == '\b')
            {
                if(BPOS)
                {
                    printf("\b \b");
                    key_cmd_buf[--BPOS] = 0x00;
                }
                
                break;
            }
            
            if(key == '\n')
            {
                key_cmd_buf[BPOS] = 0;
                key_buf[buf_pos_head-1] = 0;
                buf_pos_tail = buf_pos_head = 0;
                
                parse_shell_command();
                
                cls();
                
                info_bar_printf("Type \"help\" for the list of available commands.");
                printf("#] ");
                BPOS = 0;
                break;
            }
                
            key_cmd_buf[BPOS++] = key;        
            printf("%c", key);
        }
    }
    kill_task(active_task_id);
}
